package com.cw.lang.redis.service;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.BulkMapper;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.query.SortQuery;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.lang.Nullable;

/**
 * 操作跟key相关以及跟事务相关的
 * {@link RedisOperations}<br>
 *
 * @author chenw
 * @version $$Id: RedisService.java, v 0.1 2018/10/18 下午6:51 chenw Exp $$
 */
public interface RedisService<K extends Serializable, V extends Serializable> {

    // -------------------------------------------------------------------------
    // Methods getting services
    // -------------------------------------------------------------------------

    /**
     * 获取{@link RedisForGeoService}
     *
     * @return {@link RedisForGeoService}
     */
    <HK, HV> RedisForHashService<K, HK, HV> opsForHash();

    /**
     * 获取{@link RedisForGeoService}
     *
     * @return {@link RedisForGeoService}
     */
    RedisForGeoService<K, V> opsForGeo();

    /**
     * 获取{@link RedisForListService}
     *
     * @return {@link RedisForListService}
     */
    RedisForListService<K, V> opsForList();

    /**
     * 获取{@link RedisForSetService}
     *
     * @return {@link RedisForSetService}
     */
    RedisForSetService<K, V> opsForSet();

    /**
     * 获取{@link RedisForValueService}
     *
     * @return {@link RedisForValueService}
     */
    RedisForValueService<K, V> opsForValue();

    /**
     * 获取{@link RedisForZSetService}
     *
     * @return {@link RedisForZSetService}
     */
    RedisForZSetService<K, V> opsForZSet();

    // -------------------------------------------------------------------------
    // Methods dealing with Redis Keys
    // -------------------------------------------------------------------------

    /**
     * Determine if given {@code key} exists.
     *
     * @param key must not be {@literal null}.
     * @return Boolean
     * @see <a href="http://redis.io/commands/exists">Redis Documentation: EXISTS</a>
     */
    @Nullable
    Boolean hasKey(K key);

    /**
     * Delete given {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return {@literal true} if the key was removed.
     * @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
     */
    @Nullable
    Boolean delete(K key);

    /**
     * Delete given {@code keys}.
     *
     * @param keys must not be {@literal null}.
     * @return The number of keys that were removed. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/del">Redis Documentation: DEL</a>
     */
    @Nullable
    Long delete(Collection<K> keys);

    /**
     * Determine the type stored at {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/type">Redis Documentation: TYPE</a>
     */
    @Nullable
    DataType type(K key);

    /**
     * Find all keys matching the given {@code pattern}.
     *
     * @param pattern must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/keys">Redis Documentation: KEYS</a>
     */
    @Nullable
    Set<K> keys(K pattern);

    /**
     * Return a random key from the keyspace.
     *
     * @return {@literal null} no keys exist or when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/randomkey">Redis Documentation: RANDOMKEY</a>
     */
    @Nullable
    K randomKey();

    /**
     * Rename key {@code oldKey} to {@code newKey}.
     *
     * @param oldKey must not be {@literal null}.
     * @param newKey must not be {@literal null}.
     * @see <a href="http://redis.io/commands/rename">Redis Documentation: RENAME</a>
     */
    void rename(K oldKey, K newKey);

    /**
     * Rename key {@code oleName} to {@code newKey} only if {@code newKey} does not exist.
     *
     * @param oldKey must not be {@literal null}.
     * @param newKey must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/renamenx">Redis Documentation: RENAMENX</a>
     */
    @Nullable
    Boolean renameIfAbsent(K oldKey, K newKey);

    /**
     * Set time to live for given {@code key}..
     *
     * @param key must not be {@literal null}.
     * @param timeout timeout
     * @param unit must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     */
    @Nullable
    Boolean expire(K key, long timeout, TimeUnit unit);

    /**
     * Set the expiration for given {@code key} as a {@literal date} timestamp.
     *
     * @param key must not be {@literal null}.
     * @param date must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     */
    @Nullable
    Boolean expireAt(K key, Date date);

    /**
     * Remove the expiration from given {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/persist">Redis Documentation: PERSIST</a>
     */
    @Nullable
    Boolean persist(K key);

    /**
     * Move given {@code key} to database with {@code index}.
     *
     * @param key must not be {@literal null}.
     * @param dbIndex dbIndex
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/move">Redis Documentation: MOVE</a>
     */
    @Nullable
    Boolean move(K key, int dbIndex);

    /**
     * Retrieve serialized version of the value stored at {@code key}.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/dump">Redis Documentation: DUMP</a>
     */
    @Nullable
    byte[] dump(K key);

    /**
     * Create {@code key} using the {@code serializedValue}, previously obtained using {@link #dump(Serializable)}.
     *
     * @param key must not be {@literal null}.
     * @param value must not be {@literal null}.
     * @param timeToLive timeToLive
     * @param unit must not be {@literal null}.
     * @see <a href="http://redis.io/commands/restore">Redis Documentation: RESTORE</a>
     */
    void restore(K key, byte[] value, long timeToLive, TimeUnit unit);

    /**
     * Get the time to live for {@code key} in seconds.
     *
     * @param key must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/ttl">Redis Documentation: TTL</a>
     */
    @Nullable
    Long getExpire(K key);

    /**
     * Get the time to live for {@code key} in and converter it to the given {@link TimeUnit}.
     *
     * @param key must not be {@literal null}.
     * @param timeUnit must not be {@literal null}.
     * @return {@literal null} when used in pipeline / transaction.
     * @since 1.8
     */
    @Nullable
    Long getExpire(K key, TimeUnit timeUnit);

    /**
     * Sort the elements for {@code query}.
     *
     * @param query must not be {@literal null}.
     * @return the results of sort. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/sort">Redis Documentation: SORT</a>
     */
    @Nullable
    List<V> sort(SortQuery<K> query);

    /**
     * Sort the elements for {@code query} applying {@link RedisSerializer}.
     *
     * @param query must not be {@literal null}.
     * @return the deserialized results of sort. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/sort">Redis Documentation: SORT</a>
     */
    @Nullable
    <T> List<T> sort(SortQuery<K> query, RedisSerializer<T> resultSerializer);

    /**
     * Sort the elements for {@code query} applying {@link BulkMapper}.
     *
     * @param query must not be {@literal null}.
     * @return the deserialized results of sort. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/sort">Redis Documentation: SORT</a>
     */
    @Nullable
    <T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper);

    /**
     * Sort the elements for {@code query} applying {@link BulkMapper} and {@link RedisSerializer}.
     *
     * @param query must not be {@literal null}.
     * @return the deserialized results of sort. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/sort">Redis Documentation: SORT</a>
     */
    @Nullable
    <T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, RedisSerializer<S> resultSerializer);

    /**
     * Sort the elements for {@code query} and store result in {@code storeKey}.
     *
     * @param query must not be {@literal null}.
     * @param storeKey must not be {@literal null}.
     * @return number of values. {@literal null} when used in pipeline / transaction.
     * @see <a href="http://redis.io/commands/sort">Redis Documentation: SORT</a>
     */
    @Nullable
    Long sort(SortQuery<K> query, K storeKey);

    // -------------------------------------------------------------------------
    // Methods dealing with Redis Transactions
    // -------------------------------------------------------------------------

    /**
     * Watch given {@code key} for modifications during transaction started with {@link #multi()}.
     *
     * @param key must not be {@literal null}.
     * @see <a href="http://redis.io/commands/watch">Redis Documentation: WATCH</a>
     */
    void watch(K key);

    /**
     * Watch given {@code keys} for modifications during transaction started with {@link #multi()}.
     *
     * @param keys must not be {@literal null}.
     * @see <a href="http://redis.io/commands/watch">Redis Documentation: WATCH</a>
     */
    void watch(Collection<K> keys);

    /**
     * Flushes all the previously {@link #watch(Serializable)} keys.
     *
     * @see <a href="http://redis.io/commands/unwatch">Redis Documentation: UNWATCH</a>
     */
    void unwatch();

    /**
     * Mark the start of a transaction block. <br>
     * Commands will be queued and can then be executed by calling {@link #exec()} or rolled back using
     * {@link #discard()}
     * <p>
     *
     * @see <a href="http://redis.io/commands/multi">Redis Documentation: MULTI</a>
     */
    void multi();

    /**
     * Discard all commands issued after {@link #multi()}.
     *
     * @see <a href="http://redis.io/commands/discard">Redis Documentation: DISCARD</a>
     */
    void discard();

    /**
     * Executes all queued commands in a transaction started with {@link #multi()}. <br>
     * If used along with {@link #watch(Serializable)} the operation will fail if any of watched keys has been modified.
     *
     * @return List of replies for each executed command.
     * @see <a href="http://redis.io/commands/exec">Redis Documentation: EXEC</a>
     */
    List<Object> exec();

}
